using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Rating;
using Newtonsoft.Json.Linq;

namespace BTCPayServer.Services.Rates
{
    public class CoinGeckoRateProvider : IRateProvider
    {
        public const string CoinGeckoName = "coingecko";
        
        internal static readonly Dictionary<string, RateSourceInfo> SupportedExchanges;

        static CoinGeckoRateProvider()
        {
            // https://api.coingecko.com/api/v3/exchanges/list
            SupportedExchanges = JArray.Parse("[{\"id\":\"1bch\",\"name\":\"1BCH\"},{\"id\":\"3xcalibur\",\"name\":\"3xcalibur\"},{\"id\":\"aave\",\"name\":\"Aave\"},{\"id\":\"aax\",\"name\":\"AAX\"},{\"id\":\"aax_futures\",\"name\":\"AAX Futures\"},{\"id\":\"abcc\",\"name\":\"ABCC\"},{\"id\":\"acala_swap\",\"name\":\"Acala Swap\"},{\"id\":\"acdx\",\"name\":\"ACDX\"},{\"id\":\"ace\",\"name\":\"Ace\"},{\"id\":\"acsi_finance\",\"name\":\"Acsi Finance\"},{\"id\":\"agora_swap\",\"name\":\"Agora Swap\"},{\"id\":\"alexgo\",\"name\":\"ALEX\"},{\"id\":\"algebra_finance\",\"name\":\"Algebra finance\"},{\"id\":\"alpha_five\",\"name\":\"Alpha5\"},{\"id\":\"altcointrader\",\"name\":\"AltcoinTrader\"},{\"id\":\"alterdice\",\"name\":\"AlterDice\"},{\"id\":\"altmarkets\",\"name\":\"Altmarkets\"},{\"id\":\"amaterasu\",\"name\":\"Amaterasu Finance\"},{\"id\":\"apeswap_bsc\",\"name\":\"ApeSwap\"},{\"id\":\"apeswap_polygon\",\"name\":\"ApeSwap (Polygon)\"},{\"id\":\"apeswap_telos\",\"name\":\"Apeswap (Telos)\"},{\"id\":\"apex_pro\",\"name\":\"ApeX Pro\"},{\"id\":\"aprobit\",\"name\":\"Aprobit\"},{\"id\":\"arthswap\",\"name\":\"ArthSwap\"},{\"id\":\"astroport\",\"name\":\"Astroport (Classic)\"},{\"id\":\"auroraswap\",\"name\":\"AuroraSwap\"},{\"id\":\"autoshark_finance\",\"name\":\"AutoShark Finance\"},{\"id\":\"azbit\",\"name\":\"Azbit\"},{\"id\":\"b2bx\",\"name\":\"B2BX\"},{\"id\":\"babydogeswap\",\"name\":\"BabyDogeSwap\"},{\"id\":\"babyswap\",\"name\":\"BabySwap\"},{\"id\":\"baguette\",\"name\":\"Baguette\"},{\"id\":\"bakeryswap\",\"name\":\"Bakeryswap\"},{\"id\":\"bakkt\",\"name\":\"Bakkt\"},{\"id\":\"balanced_network\",\"name\":\"Balanced Network\"},{\"id\":\"balancer\",\"name\":\"Balancer (v2)\"},{\"id\":\"balancer_arbitrum\",\"name\":\"Balancer (Arbitrum)\"},{\"id\":\"balancer_polygon\",\"name\":\"Balancer (Polygon)\"},{\"id\":\"balancer_v1\",\"name\":\"Balancer (v1)\"},{\"id\":\"bancor\",\"name\":\"Bancor (V2)\"},{\"id\":\"bancor_v3\",\"name\":\"Bancor (V3)\"},{\"id\":\"baryon_network\",\"name\":\"Baryon Network\"},{\"id\":\"basefex\",\"name\":\"BaseFEX\"},{\"id\":\"beamswap\",\"name\":\"Beamswap\"},{\"id\":\"beaxy\",\"name\":\"Beaxy\"},{\"id\":\"beethovenx\",\"name\":\"Beethoven X\"},{\"id\":\"benswap_smart_bitcoin_cash\",\"name\":\"Benswap\"},{\"id\":\"bgogo\",\"name\":\"Bgogo\"},{\"id\":\"bibox\",\"name\":\"Bibox\"},{\"id\":\"bibox_futures\",\"name\":\"Bibox (Futures)\"},{\"id\":\"biconomy\",\"name\":\"Biconomy\"},{\"id\":\"bigone\",\"name\":\"BigONE\"},{\"id\":\"bigone_futures\",\"name\":\"BigONE Futures\"},{\"id\":\"bilaxy\",\"name\":\"Bilaxy\"},{\"id\":\"binance\",\"name\":\"Binance\"},{\"id\":\"binance_dex\",\"name\":\"Binance DEX\"},{\"id\":\"binance_dex_mini\",\"name\":\"Binance DEX (Mini)\"},{\"id\":\"binance_futures\",\"name\":\"Binance (Futures)\"},{\"id\":\"binance_us\",\"name\":\"Binance US\"},{\"id\":\"bingx\",\"name\":\"BingX\"},{\"id\":\"bingx_futures\",\"name\":\"BingX (Futures)\"},{\"id\":\"bione\",\"name\":\"BiONE\"},{\"id\":\"birake\",\"name\":\"Birake\"},{\"id\":\"bisq\",\"name\":\"Bisq\"},{\"id\":\"biswap\",\"name\":\"Biswap\"},{\"id\":\"bit2c\",\"name\":\"Bit2c\"},{\"id\":\"bitazza\",\"name\":\"Bitazza\"},{\"id\":\"bitbank\",\"name\":\"Bitbank\"},{\"id\":\"bitbay\",\"name\":\"Zonda\"},{\"id\":\"bitbegin\",\"name\":\"Bitbegin\"},{\"id\":\"bitbns\",\"name\":\"BitBNS\"},{\"id\":\"bitbox\",\"name\":\"BITFRONT\"},{\"id\":\"bitbuy\",\"name\":\"Bitbuy\"},{\"id\":\"bitci\",\"name\":\"Bitci\"},{\"id\":\"bitcoin_com\",\"name\":\"FMFW.io\"},{\"id\":\"bit_com\",\"name\":\"BIT\"},{\"id\":\"bit_com_futures\",\"name\":\"BIT (Futures)\"},{\"id\":\"bitexbook\",\"name\":\"BITEXBOOK\"},{\"id\":\"bitexen\",\"name\":\"Bitexen\"},{\"id\":\"bitexlive\",\"name\":\"Bitexlive\"},{\"id\":\"bitfex\",\"name\":\"Bitfex\"},{\"id\":\"bitfinex\",\"name\":\"Bitfinex\"},{\"id\":\"bitfinex_futures\",\"name\":\"Bitfinex (Futures)\"},{\"id\":\"bitflyer\",\"name\":\"bitFlyer\"},{\"id\":\"bitflyer_futures\",\"name\":\"Bitflyer (Futures)\"},{\"id\":\"bitforex\",\"name\":\"Bitforex\"},{\"id\":\"bitforex_futures\",\"name\":\"Bitforex (Futures)\"},{\"id\":\"bitget\",\"name\":\"Bitget\"},{\"id\":\"bitget_futures\",\"name\":\"Bitget Futures\"},{\"id\":\"bithash\",\"name\":\"BitHash\"},{\"id\":\"bithumb\",\"name\":\"Bithumb\"},{\"id\":\"bithumb_futures\",\"name\":\"Bithumb (Futures)\"},{\"id\":\"bithumb_global\",\"name\":\"BitGlobal\"},{\"id\":\"bitinka\",\"name\":\"Bitinka.com\"},{\"id\":\"bitkonan\",\"name\":\"BitKonan\"},{\"id\":\"bitkub\",\"name\":\"Bitkub\"},{\"id\":\"bitlo\",\"name\":\"Bitlo\"},{\"id\":\"bitmart\",\"name\":\"BitMart\"},{\"id\":\"bitmart_futures\",\"name\":\"Bitmart Futures\"},{\"id\":\"bitmax\",\"name\":\"AscendEX (BitMax)\"},{\"id\":\"bitmax_futures\",\"name\":\"AscendEX  (BitMax) (Futures)\"},{\"id\":\"bitmex\",\"name\":\"BitMEX (Derivative)\"},{\"id\":\"bitmex_spot\",\"name\":\"BitMEX\"},{\"id\":\"bitoffer\",\"name\":\"Bitoffer\"},{\"id\":\"bitonbay\",\"name\":\"BitOnBay\"},{\"id\":\"bitopro\",\"name\":\"BitoPro\"},{\"id\":\"bitpanda\",\"name\":\"Bitpanda Pro\"},{\"id\":\"bitrue\",\"name\":\"Bitrue\"},{\"id\":\"bitrue_futures\",\"name\":\"Bitrue (Futures)\"},{\"id\":\"bitso\",\"name\":\"Bitso\"},{\"id\":\"bitstamp\",\"name\":\"Bitstamp\"},{\"id\":\"bitsten\",\"name\":\"Bitsten\"},{\"id\":\"bitstorage\",\"name\":\"BitStorage\"},{\"id\":\"bittrex\",\"name\":\"Bittrex\"},{\"id\":\"bitubu\",\"name\":\"Bitubu Exchange\"},{\"id\":\"bitvavo\",\"name\":\"Bitvavo\"},{\"id\":\"bitz_futures\",\"name\":\"BitZ (Futures)\"},{\"id\":\"bkex\",\"name\":\"BKEX\"},{\"id\":\"blockchain_com\",\"name\":\"Blockchain.com\"},{\"id\":\"bossswap\",\"name\":\"BossSwap\"},{\"id\":\"btc_alpha\",\"name\":\"BTC-Alpha\"},{\"id\":\"btcbox\",\"name\":\"BTCBOX\"},{\"id\":\"btcc\",\"name\":\"BTCC\"},{\"id\":\"btcc_futures\",\"name\":\"BTCC Futures\"},{\"id\":\"btcex\",\"name\":\"BTCEX\"},{\"id\":\"btcex_futures\",\"name\":\"BTCEX (Futures)\"},{\"id\":\"btcmarkets\",\"name\":\"BTCMarkets\"},{\"id\":\"btcmex\",\"name\":\"BTCMEX\"},{\"id\":\"btcsquare\",\"name\":\"BTCSquare\"},{\"id\":\"btc_trade_ua\",\"name\":\"BTC Trade UA\"},{\"id\":\"btcturk\",\"name\":\"BtcTurk PRO\"},{\"id\":\"btse\",\"name\":\"BTSE\"},{\"id\":\"btse_futures\",\"name\":\"BTSE (Futures)\"},{\"id\":\"bullish_com\",\"name\":\"Bullish\"},{\"id\":\"buyucoin\",\"name\":\"BuyUcoin\"},{\"id\":\"bw\",\"name\":\"BW.com\"},{\"id\":\"bybit\",\"name\":\"Bybit (Futures)\"},{\"id\":\"bybit_spot\",\"name\":\"Bybit\"},{\"id\":\"camelot\",\"name\":\"Camelot\"},{\"id\":\"canto_dex\",\"name\":\"Canto Dex\"},{\"id\":\"capricorn\",\"name\":\"Capricorn\"},{\"id\":\"catex\",\"name\":\"Catex\"},{\"id\":\"cbx\",\"name\":\"CBX\"},{\"id\":\"ccex\",\"name\":\"C-CEX\"},{\"id\":\"cex\",\"name\":\"CEX.IO\"},{\"id\":\"chainex\",\"name\":\"ChainEX\"},{\"id\":\"changelly\",\"name\":\"Changelly PRO\"},{\"id\":\"cherryswap\",\"name\":\"CherrySwap\"},{\"id\":\"chiliz\",\"name\":\"ChilizX\"},{\"id\":\"citex\",\"name\":\"CITEX\"},{\"id\":\"claimswap\",\"name\":\"Claimswap\"},{\"id\":\"clipper_ethereum\",\"name\":\"Clipper (Ethereum)\"},{\"id\":\"clipper_moonbeam\",\"name\":\"Clipper (Moonbeam)\"},{\"id\":\"clipper_optimism\",\"name\":\"Clipper (Optimism)\"},{\"id\":\"clipper_polygon\",\"name\":\"Clipper (Polygon)\"},{\"id\":\"cme_futures\",\"name\":\"CME Group\"},{\"id\":\"coinbene\",\"name\":\"CoinBene\"},{\"id\":\"coincheck\",\"name\":\"Coincheck\"},{\"id\":\"coindcx\",\"name\":\"CoinDCX\"},{\"id\":\"coindeal\",\"name\":\"Coindeal\"},{\"id\":\"coin_egg\",\"name\":\"CoinEgg\"},{\"id\":\"coinex\",\"name\":\"CoinEx\"},{\"id\":\"coinex_futures\",\"name\":\"CoinEx (Futures)\"},{\"id\":\"coinfalcon\",\"name\":\"Coinfalcon\"},{\"id\":\"coinfield\",\"name\":\"Coinfield\"},{\"id\":\"coinflex\",\"name\":\"CoinFLEX\"},{\"id\":\"coinflex_futures\",\"name\":\"CoinFLEX (Futures)\"},{\"id\":\"coingi\",\"name\":\"Coingi\"},{\"id\":\"coinjar\",\"name\":\"CoinJar Exchange\"},{\"id\":\"coinlist\",\"name\":\"Coinlist\"},{\"id\":\"coinmargin\",\"name\":\"CoinMargin\"},{\"id\":\"coin_metro\",\"name\":\"Coinmetro\"},{\"id\":\"coinone\",\"name\":\"Coinone\"},{\"id\":\"coinsbit\",\"name\":\"Coinsbit\"},{\"id\":\"coinspro\",\"name\":\"Coins.ph\"},{\"id\":\"coinstore\",\"name\":\"Coinstore\"},{\"id\":\"cointiger\",\"name\":\"CoinTiger\"},{\"id\":\"cointiger_futures\",\"name\":\"CoinTiger (Futures)\"},{\"id\":\"cointr\",\"name\":\"CoinTR Pro\"},{\"id\":\"coinzix\",\"name\":\"Coinzix\"},{\"id\":\"coinzoom\",\"name\":\"Coinzoom\"},{\"id\":\"comethswap\",\"name\":\"ComethSwap\"},{\"id\":\"concave\",\"name\":\"Concave\"},{\"id\":\"c_patex\",\"name\":\"C-Patex\"},{\"id\":\"crema_finance\",\"name\":\"Crema Finance\"},{\"id\":\"crescent\",\"name\":\"Crescent\"},{\"id\":\"crex24\",\"name\":\"CREX24\"},{\"id\":\"crodex\",\"name\":\"Crodex\"},{\"id\":\"cronaswap\",\"name\":\"Cronaswap\"},{\"id\":\"cronus_finance\",\"name\":\"Cronus Finance\"},{\"id\":\"crypto_com\",\"name\":\"Crypto.com Exchange\"},{\"id\":\"crypto_com_futures\",\"name\":\"Crypto.com Exchange (Futures)\"},{\"id\":\"cryptology\",\"name\":\"Cryptology\"},{\"id\":\"c_trade\",\"name\":\"C-Trade\"},{\"id\":\"currency\",\"name\":\"Currency.com\"},{\"id\":\"curve_arbitrum\",\"name\":\"Curve (Arbitrum)\"},{\"id\":\"curve_avalanche\",\"name\":\"Curve (Avalanche)\"},{\"id\":\"curve_ethereum\",\"name\":\"Curve (Ethereum)\"},{\"id\":\"curve_fantom\",\"name\":\"Curve (Fantom)\"},{\"id\":\"curve_optimism\",\"name\":\"Curve (Optimism)\"},{\"id\":\"curve_polygon\",\"name\":\"Curve (Polygon)\"},{\"id\":\"curve_xdai\",\"name\":\"Curve (Xdai)\"},{\"id\":\"dao_swap\",\"name\":\"DAO Swap\"},{\"id\":\"darkknight\",\"name\":\"Dark KnightSwap\"},{\"id\":\"dcoin\",\"name\":\"Dcoin\"},{\"id\":\"decoin\",\"name\":\"Decoin\"},{\"id\":\"deepcoin\",\"name\":\"Deepcoin\"},{\"id\":\"deepcoin_derivatives\",\"name\":\"Deepcoin (Derivatives)\"},{\"id\":\"defichain\",\"name\":\"DeFiChain DEX\"},{\"id\":\"defi_kingdoms\",\"name\":\"Defi Kingdoms\"},{\"id\":\"defi_kingdoms_crystalvale\",\"name\":\"Defi Kingdoms (Crystalvale)\"},{\"id\":\"defi_plaza\",\"name\":\"DefiPlaza\"},{\"id\":\"defi_swap\",\"name\":\"DeFi Swap\"},{\"id\":\"delta_futures\",\"name\":\"Delta Exchange (Futures)\"},{\"id\":\"delta_spot\",\"name\":\"Delta Exchange\"},{\"id\":\"dem_exchange\",\"name\":\"Demex\"},{\"id\":\"deribit\",\"name\":\"Deribit\"},{\"id\":\"deversifi\",\"name\":\"Rhino.fi\"},{\"id\":\"dexalot\",\"name\":\"Dexalot\"},{\"id\":\"dextrade\",\"name\":\"Dex-Trade\"},{\"id\":\"dfx\",\"name\":\"DFX\"},{\"id\":\"dfx_polygon\",\"name\":\"DFX (Polygon)\"},{\"id\":\"dfyn\",\"name\":\"Dfyn\"},{\"id\":\"diffusion\",\"name\":\"Diffusion Finance\"},{\"id\":\"digifinex\",\"name\":\"DigiFinex\"},{\"id\":\"dodo\",\"name\":\"DODO\"},{\"id\":\"dodo_arbitrum\",\"name\":\"Dodo (Arbitrum)\"},{\"id\":\"dodo_bsc\",\"name\":\"Dodo (BSC)\"},{\"id\":\"dodo_polygon\",\"name\":\"Dodo (Polygon)\"},{\"id\":\"dogeshrek\",\"name\":\"Dogeshrek\"},{\"id\":\"dogeswap\",\"name\":\"DogeSwap\"},{\"id\":\"dooar_bsc\",\"name\":\"DOOAR (BSC)\"},{\"id\":\"dooar_ethereum\",\"name\":\"DOOAR (Ethereum)\"},{\"id\":\"dove_wallet\",\"name\":\"BTX\"},{\"id\":\"drift_protocol\",\"name\":\"Drift Protocol\"},{\"id\":\"duckydefi\",\"name\":\"DuckyDeFi\"},{\"id\":\"duedex\",\"name\":\"DueDEX\"},{\"id\":\"dydx\",\"name\":\"dYdX\"},{\"id\":\"dydx_perpetual\",\"name\":\"dYdX Perpetual\"},{\"id\":\"dystopia\",\"name\":\"Dystopia\"},{\"id\":\"elk_finance_avax\",\"name\":\"Elk Finance (Avalanche)\"},{\"id\":\"elk_finance_bsc\",\"name\":\"Elk Finance (BSC)\"},{\"id\":\"elk_finance_ethereum\",\"name\":\"Elk Finance (Ethereum)\"},{\"id\":\"elk_finance_polygon\",\"name\":\"Elk Finance (Polygon)\"},{\"id\":\"elk_finance_telos\",\"name\":\"Elk Finance (Telos)\"},{\"id\":\"emirex\",\"name\":\"Emirex\"},{\"id\":\"empiredex\",\"name\":\"EmpireDEX (Cronos)\"},{\"id\":\"empiredex_bsc\",\"name\":\"EmpireDEX (BSC)\"},{\"id\":\"energiswap\",\"name\":\"Energiswap\"},{\"id\":\"equalizer\",\"name\":\"Equalizer\"},{\"id\":\"equos\",\"name\":\"EQONEX\"},{\"id\":\"equos_perpetual\",\"name\":\"EQONEX (Perpetual)\"},{\"id\":\"evmoswap\",\"name\":\"EvmoSwap\"},{\"id\":\"excalibur\",\"name\":\"Excalibur\"},{\"id\":\"exmarkets\",\"name\":\"ExMarkets\"},{\"id\":\"exmo\",\"name\":\"EXMO\"},{\"id\":\"fairyswap\",\"name\":\"Fairyswap\"},{\"id\":\"fameex\",\"name\":\"Fameex\"},{\"id\":\"fatbtc\",\"name\":\"FatBTC\"},{\"id\":\"finexbox\",\"name\":\"FinexBox\"},{\"id\":\"firebird_finance_polygon\",\"name\":\"Firebird Finance (Polygon)\"},{\"id\":\"flatqube\",\"name\":\"FlatQube\"},{\"id\":\"flybit\",\"name\":\"Flybit\"},{\"id\":\"four_swap\",\"name\":\"4swap\"},{\"id\":\"foxbit\",\"name\":\"Foxbit\"},{\"id\":\"fraxswap_ethereum\",\"name\":\"Fraxswap (Ethereum)\"},{\"id\":\"freiexchange\",\"name\":\"Freiexchange\"},{\"id\":\"fubt\",\"name\":\"FUBT\"},{\"id\":\"fuzz_finance\",\"name\":\"FuzzSwap\"},{\"id\":\"fx_swap\",\"name\":\"Fx Swap\"},{\"id\":\"gate\",\"name\":\"Gate.io\"},{\"id\":\"gate_futures\",\"name\":\"Gate.io (Futures)\"},{\"id\":\"gdac\",\"name\":\"GDAC\"},{\"id\":\"gdax\",\"name\":\"Coinbase Exchange\"},{\"id\":\"gemini\",\"name\":\"Gemini\"},{\"id\":\"glide_finance\",\"name\":\"Glide Finance\"},{\"id\":\"globe_exchange\",\"name\":\"Globe\"},{\"id\":\"globe_exchange_derivatives\",\"name\":\"Globe (Derivatives)\"},{\"id\":\"gmo_japan\",\"name\":\"GMO Japan\"},{\"id\":\"gmo_japan_futures\",\"name\":\"GMO Japan (Futures)\"},{\"id\":\"goku\",\"name\":\"GokuMarket\"},{\"id\":\"gopax\",\"name\":\"GoPax\"},{\"id\":\"graviex\",\"name\":\"Graviex\"},{\"id\":\"gravity_finance\",\"name\":\"Gravity Finance\"},{\"id\":\"greenhouse_dex\",\"name\":\"Greenhouse\"},{\"id\":\"hakuswap\",\"name\":\"HakuSwap\"},{\"id\":\"hanbitco\",\"name\":\"Hanbitco\"},{\"id\":\"hbtc_futures\",\"name\":\"BHEX (Futures)\"},{\"id\":\"hebeswap\",\"name\":\"Hebeswap\"},{\"id\":\"hermes_protocol\",\"name\":\"Hermes Protocol\"},{\"id\":\"hitbtc\",\"name\":\"HitBTC\"},{\"id\":\"hitbtc_derivatives\",\"name\":\"HitBTC (Derivatives)\"},{\"id\":\"honeyswap\",\"name\":\"Honeyswap\"},{\"id\":\"honeyswap_polygon\",\"name\":\"Honeyswap (Polygon)\"},{\"id\":\"hoo\",\"name\":\"Hoo.com\"},{\"id\":\"hopex\",\"name\":\"Hopex\"},{\"id\":\"hotbit\",\"name\":\"Hotbit\"},{\"id\":\"huckleberry\",\"name\":\"Huckleberry\"},{\"id\":\"huobi\",\"name\":\"Huobi\"},{\"id\":\"huobi_dm\",\"name\":\"Huobi Futures\"},{\"id\":\"huobi_japan\",\"name\":\"Huobi Japan\"},{\"id\":\"huobi_korea\",\"name\":\"Huobi Korea\"},{\"id\":\"hydra\",\"name\":\"Hydra DEX\"},{\"id\":\"idex\",\"name\":\"Idex\"},{\"id\":\"impossible_finance\",\"name\":\"Impossible Finance\"},{\"id\":\"impossible_finance_v3\",\"name\":\"Impossible Finance (v3)\"},{\"id\":\"independent_reserve\",\"name\":\"Independent Reserve\"},{\"id\":\"indodax\",\"name\":\"Indodax\"},{\"id\":\"injective\",\"name\":\"Helix\"},{\"id\":\"injective_futures\",\"name\":\"Helix (Futures)\"},{\"id\":\"integral_size\",\"name\":\"Integral SIZE\"},{\"id\":\"itbit\",\"name\":\"itBit\"},{\"id\":\"iziswap\",\"name\":\"Iziswap\"},{\"id\":\"jetswap\",\"name\":\"JetSwap\"},{\"id\":\"jex_futures\",\"name\":\"Binance JEX (Futures)\"},{\"id\":\"jswap\",\"name\":\"Jswap\"},{\"id\":\"julswap\",\"name\":\"Julswap\"},{\"id\":\"junoswap\",\"name\":\"Junoswap\"},{\"id\":\"jupiter\",\"name\":\"Jupiter\"},{\"id\":\"kaidex\",\"name\":\"Kaidex\"},{\"id\":\"kaidex_v3\",\"name\":\"Kaidex V3\"},{\"id\":\"kanga\",\"name\":\"Kanga\"},{\"id\":\"karura_swap\",\"name\":\"Karura Swap\"},{\"id\":\"katana\",\"name\":\"Katana\"},{\"id\":\"kava\",\"name\":\"Kava Swap\"},{\"id\":\"kdswap\",\"name\":\"KDSwap\"},{\"id\":\"khaos_exchange\",\"name\":\"Khaos Exchange\"},{\"id\":\"kibbleswap\",\"name\":\"KibbleSwap\"},{\"id\":\"kickex\",\"name\":\"KickEX\"},{\"id\":\"klayswap\",\"name\":\"KLAYSwap\"},{\"id\":\"klever_exchange\",\"name\":\"Klever Exchange\"},{\"id\":\"klex\",\"name\":\"KLEX\"},{\"id\":\"knightswap\",\"name\":\"KnightSwap\"},{\"id\":\"koinbazar\",\"name\":\"Koinbazar\"},{\"id\":\"korbit\",\"name\":\"Korbit\"},{\"id\":\"kraken\",\"name\":\"Kraken\"},{\"id\":\"kraken_futures\",\"name\":\"Kraken (Futures)\"},{\"id\":\"kucoin\",\"name\":\"KuCoin\"},{\"id\":\"kujira\",\"name\":\"Kujira Fin\"},{\"id\":\"kumex\",\"name\":\"KuCoin Futures\"},{\"id\":\"kuna\",\"name\":\"Kuna Exchange\"},{\"id\":\"kuswap\",\"name\":\"Kuswap\"},{\"id\":\"kyberswap_classic_avalanche\",\"name\":\"KyberSwap Classic (Avalanche)\"},{\"id\":\"kyberswap_classic_bsc\",\"name\":\"KyberSwap Classic (BSC)\"},{\"id\":\"kyberswap_classic_bttc\",\"name\":\"Kyberswap Classic (Bittorent)\"},{\"id\":\"kyberswap_classic_ethereum\",\"name\":\"KyberSwap Classic (Ethereum)\"},{\"id\":\"kyberswap_classic_fantom\",\"name\":\"KyberSwap Classic (Fantom)\"},{\"id\":\"kyberswap_classic_polygon\",\"name\":\"KyberSwap Classic (Polygon)\"},{\"id\":\"kyberswap_elastic\",\"name\":\"KyberSwap Elastic (Ethereum)\"},{\"id\":\"kyberswap_elastic_arbitrum\",\"name\":\"Kyberswap Elastic (Arbitrum)\"},{\"id\":\"kyberswap_elastic_avalanche\",\"name\":\"Kyberswap Elastic (Avalanche)\"},{\"id\":\"kyberswap_elastic_bsc\",\"name\":\"Kyberswap Elastic (BSC)\"},{\"id\":\"kyberswap_elastic_fantom\",\"name\":\"Kyberswap Elastic (Fantom)\"},{\"id\":\"kyberswap_elastic_optimism\",\"name\":\"Kyberswap Elastic (Optimism)\"},{\"id\":\"kyberswap_elastic_polygon\",\"name\":\"Kyberswap Elastic (Polygon)\"},{\"id\":\"latoken\",\"name\":\"LATOKEN\"},{\"id\":\"lbank\",\"name\":\"LBank\"},{\"id\":\"lcx\",\"name\":\"LCX Exchange\"},{\"id\":\"leonicornswap\",\"name\":\"LeonicornSwap\"},{\"id\":\"levinswap_xdai\",\"name\":\"Levinswap (xDai)\"},{\"id\":\"lfgswap\",\"name\":\"LFGswap\"},{\"id\":\"lif3\",\"name\":\"LIF3 (Tombchain)\"},{\"id\":\"lif3-bsc\",\"name\":\"LIF3 (BSC)\"},{\"id\":\"lif3-polygon\",\"name\":\"LIF3 (Polygon)\"},{\"id\":\"liquid_derivatives\",\"name\":\"Liquid Perpetuals\"},{\"id\":\"localtrade\",\"name\":\"LocalTrade\"},{\"id\":\"loop\",\"name\":\"Loop Markets\"},{\"id\":\"loopring\",\"name\":\"Loopring\"},{\"id\":\"loopring_amm\",\"name\":\"Loopring AMM\"},{\"id\":\"luaswap\",\"name\":\"Luaswap\"},{\"id\":\"luno\",\"name\":\"Luno\"},{\"id\":\"lydia_finance\",\"name\":\"Lydia Finance\"},{\"id\":\"lykke\",\"name\":\"Lykke\"},{\"id\":\"maiar\",\"name\":\"xExchange\"},{\"id\":\"makiswap\",\"name\":\"Makiswap\"},{\"id\":\"mango_markets_derivatives\",\"name\":\"Mango Markets (Derivatives)\"},{\"id\":\"mango_markets_spot\",\"name\":\"Mango Markets\"},{\"id\":\"mars_ecosystem\",\"name\":\"Mars Ecosystem\"},{\"id\":\"max_maicoin\",\"name\":\"Max Maicoin\"},{\"id\":\"mcdex\",\"name\":\"MCDEX (Arbitrum)\"},{\"id\":\"mcdex_bsc\",\"name\":\"MCDEX (BSC)\"},{\"id\":\"mdex\",\"name\":\"Mdex\"},{\"id\":\"mdex_bsc\",\"name\":\"Mdex BSC\"},{\"id\":\"mercado_bitcoin\",\"name\":\"Mercado Bitcoin\"},{\"id\":\"mercatox\",\"name\":\"Mercatox\"},{\"id\":\"miaswap\",\"name\":\"MiaSwap\"},{\"id\":\"milkyswap-milkada\",\"name\":\"MilkySwap\"},{\"id\":\"mimo\",\"name\":\"Mimo\"},{\"id\":\"minswap\",\"name\":\"Minswap\"},{\"id\":\"mistswap_smart_bitcoin_cash\",\"name\":\"Mistswap\"},{\"id\":\"mm_finance\",\"name\":\"MMFinance (Cronos)\"},{\"id\":\"mmfinance_polygon\",\"name\":\"MMFinance (Polygon)\"},{\"id\":\"mojitoswap\",\"name\":\"MojitoSwap\"},{\"id\":\"morpheus_swap\",\"name\":\"Morpheus Swap\"},{\"id\":\"muesliswap\",\"name\":\"Muesliswap\"},{\"id\":\"muesliswap-milkada\",\"name\":\"Muesliswap (Milkada)\"},{\"id\":\"mxc\",\"name\":\"MEXC Global\"},{\"id\":\"mxc_futures\",\"name\":\"MEXC Global (Futures)\"},{\"id\":\"mycoinstory\",\"name\":\"MCS\"},{\"id\":\"nachoswap\",\"name\":\"NachoSwap\"},{\"id\":\"namebase\",\"name\":\"Namebase\"},{\"id\":\"nami_exchange\",\"name\":\"Nami.Exchange\"},{\"id\":\"narkasa\",\"name\":\"Narkasa\"},{\"id\":\"nash\",\"name\":\"Nash\"},{\"id\":\"nearpad\",\"name\":\"NearPAD\"},{\"id\":\"negociecoins\",\"name\":\"Negociecoins\"},{\"id\":\"netswap\",\"name\":\"Netswap\"},{\"id\":\"newdex\",\"name\":\"Newdex\"},{\"id\":\"nexus_mutual\",\"name\":\"Nexus Mutual\"},{\"id\":\"nice_hash\",\"name\":\"NiceHash\"},{\"id\":\"nominex\",\"name\":\"Nominex\"},{\"id\":\"nomiswap\",\"name\":\"Nomiswap\"},{\"id\":\"nomiswap_stable\",\"name\":\"Nomiswap (Stable)\"},{\"id\":\"novadax\",\"name\":\"NovaDAX\"},{\"id\":\"oasis_trade\",\"name\":\"OasisDEX\"},{\"id\":\"occamx\",\"name\":\"OccamX\"},{\"id\":\"oceanex\",\"name\":\"Oceanex\"},{\"id\":\"okcoin\",\"name\":\"Okcoin\"},{\"id\":\"okex\",\"name\":\"OKX\"},{\"id\":\"okex_swap\",\"name\":\"OKX (Futures)\"},{\"id\":\"omgfin\",\"name\":\"Omgfin\"},{\"id\":\"omnidex\",\"name\":\"OmniDex\"},{\"id\":\"one_inch_liquidity_protocol\",\"name\":\"1inch Liquidity Protocol\"},{\"id\":\"one_inch_liquidity_protocol_bsc\",\"name\":\"1inch Liquidity Protocol (BSC)\"},{\"id\":\"oolongswap\",\"name\":\"Oolongswap\"},{\"id\":\"openleverage\",\"name\":\"OpenLeverage\"},{\"id\":\"openocean_finance\",\"name\":\"OpenOcean\"},{\"id\":\"openswap\",\"name\":\"OpenSwap\"},{\"id\":\"oraidex\",\"name\":\"OraiDEX\"},{\"id\":\"orca\",\"name\":\"Orca\"},{\"id\":\"orderbook\",\"name\":\"Orderbook.io\"},{\"id\":\"orderly_network\",\"name\":\"Orderly Network\"},{\"id\":\"osmosis\",\"name\":\"Osmosis\"},{\"id\":\"ovex\",\"name\":\"Ovex\"},{\"id\":\"p2pb2b\",\"name\":\"P2B\"},{\"id\":\"paintswap\",\"name\":\"Paintswap\"},{\"id\":\"pancakeswap_ethereum\",\"name\":\"PancakeSwap (Ethereum)\"},{\"id\":\"pancakeswap_new\",\"name\":\"PancakeSwap (v2)\"},{\"id\":\"pangolin\",\"name\":\"Pangolin\"},{\"id\":\"pangolin-flare\",\"name\":\"Pangolin (Flare)\"},{\"id\":\"pangolin-songbird\",\"name\":\"Pangolin (Songbird)\"},{\"id\":\"paribu\",\"name\":\"Paribu\"},{\"id\":\"paritex\",\"name\":\"Paritex\"},{\"id\":\"paymium\",\"name\":\"Paymium\"},{\"id\":\"pegasys\",\"name\":\"Pegasys\"},{\"id\":\"perpetual_protocol\",\"name\":\"Perpetual Protocol\"},{\"id\":\"phemex\",\"name\":\"Phemex\"},{\"id\":\"phemex_futures\",\"name\":\"Phemex (Futures)\"},{\"id\":\"photonswap\",\"name\":\"PhotonSwap (Cronos)\"},{\"id\":\"photonswap_kava\",\"name\":\"PhotonSwap (Kava)\"},{\"id\":\"pinkswap\",\"name\":\"PinkSwap\"},{\"id\":\"pionex\",\"name\":\"Pionex\"},{\"id\":\"planet_finance\",\"name\":\"Planet Finance\"},{\"id\":\"platypus_finance\",\"name\":\"Platypus Finance\"},{\"id\":\"polkaex_shiden\",\"name\":\"PolkaEx (Shiden)\"},{\"id\":\"polkaswap\",\"name\":\"Polkaswap\"},{\"id\":\"poloniex\",\"name\":\"Poloniex\"},{\"id\":\"poloniex_futures\",\"name\":\"Poloniex Futures\"},{\"id\":\"polycat_finance\",\"name\":\"Polycat Finance\"},{\"id\":\"polydex\",\"name\":\"PolyDEX\"},{\"id\":\"polyzap\",\"name\":\"PolyZap\"},{\"id\":\"pomswap\",\"name\":\"POMSwap\"},{\"id\":\"powertrade\",\"name\":\"Powertrade\"},{\"id\":\"powswap\",\"name\":\"Powswap\"},{\"id\":\"prime_xbt\",\"name\":\"Prime XBT\"},{\"id\":\"prism\",\"name\":\"Prism Protocol\"},{\"id\":\"probit\",\"name\":\"ProBit Global\"},{\"id\":\"probit_kr\",\"name\":\"Probit (Korea)\"},{\"id\":\"protofi\",\"name\":\"ProtoFi\"},{\"id\":\"puddingswap\",\"name\":\"PuddingSwap\"},{\"id\":\"purcow\",\"name\":\"Purcow\"},{\"id\":\"qtrade\",\"name\":\"qTrade\"},{\"id\":\"quickswap\",\"name\":\"Quickswap\"},{\"id\":\"quickswap_dogechain\",\"name\":\"Quickswap (Dogechain)\"},{\"id\":\"quickswap_v3\",\"name\":\"Quickswap (v3)\"},{\"id\":\"quipuswap\",\"name\":\"Quipuswap\"},{\"id\":\"quoine\",\"name\":\"Liquid\"},{\"id\":\"radioshack_avalanche\",\"name\":\"RadioShack (Avalanche)\"},{\"id\":\"radioshack_bsc\",\"name\":\"RadioShack (BSC)\"},{\"id\":\"radioshack_ethereum\",\"name\":\"RadioShack (Ethereum)\"},{\"id\":\"radioshack_polygon_pos\",\"name\":\"RadioShack (Polygon)\"},{\"id\":\"raydium2\",\"name\":\"Raydium\"},{\"id\":\"rcpswap\",\"name\":\"RCP Swap\"},{\"id\":\"ref_finance\",\"name\":\"Ref Finance\"},{\"id\":\"resfinex\",\"name\":\"Resfinex\"},{\"id\":\"ruby_exchange\",\"name\":\"Ruby Exchange\"},{\"id\":\"saber\",\"name\":\"Saber\"},{\"id\":\"safe_trade\",\"name\":\"SafeTrade\"},{\"id\":\"sakeswap\",\"name\":\"SakeSwap\"},{\"id\":\"saros\",\"name\":\"Saros Finance\"},{\"id\":\"saucerswap_dex\",\"name\":\"Saucerswap\"},{\"id\":\"secondbtc\",\"name\":\"SecondBTC\"},{\"id\":\"secretswap\",\"name\":\"SecretSwap\"},{\"id\":\"serum_dex\",\"name\":\"Serum DEX\"},{\"id\":\"sharkswap\",\"name\":\"SharkSwap\"},{\"id\":\"shibaswap\",\"name\":\"Shibaswap\"},{\"id\":\"shibswap\",\"name\":\"ShibSwap\"},{\"id\":\"siennaswap\",\"name\":\"Siennaswap\"},{\"id\":\"sifchain\",\"name\":\"Sifchain\"},{\"id\":\"sinegy\",\"name\":\"SINEGY\"},{\"id\":\"solarbeam\",\"name\":\"Solarbeam\"},{\"id\":\"solarflare\",\"name\":\"Solarflare\"},{\"id\":\"solidly\",\"name\":\"Solidly\"},{\"id\":\"solidlydex\",\"name\":\"Solidly (Ethereum)\"},{\"id\":\"soulswap\",\"name\":\"Soulswap\"},{\"id\":\"south_xchange\",\"name\":\"SouthXchange\"},{\"id\":\"sovryn\",\"name\":\"Sovryn DEX\"},{\"id\":\"spartan_protocol\",\"name\":\"Spartan Protocol\"},{\"id\":\"sphynx_brise\",\"name\":\"Sphynx (Brise)\"},{\"id\":\"sphynx_swap\",\"name\":\"Sphynx Swap (BSC)\"},{\"id\":\"spice_trade_avalanche\",\"name\":\"Spice Trade (Avalanche)\"},{\"id\":\"spicyswap\",\"name\":\"Spicyswap\"},{\"id\":\"spiritswap\",\"name\":\"SpiritSwap\"},{\"id\":\"spiritswap_v2\",\"name\":\"SpiritSwap (V2)\"},{\"id\":\"spookyswap\",\"name\":\"SpookySwap\"},{\"id\":\"stake_cube\",\"name\":\"StakeCube Exchange\"},{\"id\":\"stellar_term\",\"name\":\"StellarTerm\"},{\"id\":\"stellaswap\",\"name\":\"StellaSwap\"},{\"id\":\"step-exchange\",\"name\":\"Step Exchange\"},{\"id\":\"step_finance\",\"name\":\"Step Finance\"},{\"id\":\"stocks_exchange\",\"name\":\"STEX\"},{\"id\":\"stormgain\",\"name\":\"Stormgain\"},{\"id\":\"stormgain_futures\",\"name\":\"Stormgain Futures\"},{\"id\":\"sundaeswap\",\"name\":\"Sundaeswap\"},{\"id\":\"sunswap_v1\",\"name\":\"SUN.io\"},{\"id\":\"surfswap\",\"name\":\"Surfswap\"},{\"id\":\"sushiswap\",\"name\":\"Sushiswap\"},{\"id\":\"sushiswap_arbitrum\",\"name\":\"Sushiswap (Arbitrum One)\"},{\"id\":\"sushiswap_arbitrum_nova\",\"name\":\"Sushiswap (Arbitrum Nova)\"},{\"id\":\"sushiswap_avalanche\",\"name\":\"Sushiswap (Avalanche)\"},{\"id\":\"sushiswap_bsc\",\"name\":\"Sushiswap (BSC)\"},{\"id\":\"sushiswap_celo\",\"name\":\"Sushiswap Celo\"},{\"id\":\"sushiswap_fantom\",\"name\":\"Sushiswap (Fantom)\"},{\"id\":\"sushiswap_harmony\",\"name\":\"Sushiswap (Harmony)\"},{\"id\":\"sushiswap_polygon_pos\",\"name\":\"Sushiswap (Polygon POS)\"},{\"id\":\"sushiswap_xdai\",\"name\":\"Sushiswap (xDai)\"},{\"id\":\"swapfish\",\"name\":\"SwapFish\"},{\"id\":\"swappi\",\"name\":\"Swappi\"},{\"id\":\"swapr_arbitrum\",\"name\":\"Swapr (Arbitrum)\"},{\"id\":\"swapr_ethereum\",\"name\":\"Swapr (Ethereum)\"},{\"id\":\"swapr_xdai\",\"name\":\"Swapr (Xdai)\"},{\"id\":\"swop_fi\",\"name\":\"Swop.Fi\"},{\"id\":\"swych\",\"name\":\"SWYCH\"},{\"id\":\"synfutures\",\"name\":\"SynFutures\"},{\"id\":\"synthetix\",\"name\":\"Kwenta\"},{\"id\":\"tangoswap\",\"name\":\"TangoSwap\"},{\"id\":\"tdax\",\"name\":\"Satang Pro\"},{\"id\":\"tealswap\",\"name\":\"Tealswap\"},{\"id\":\"templedao\",\"name\":\"TempleDAO\"},{\"id\":\"tenderswap\",\"name\":\"Tenderswap\"},{\"id\":\"terraswap\",\"name\":\"Terraswap Classic\"},{\"id\":\"tethys\",\"name\":\"Tethys Finance\"},{\"id\":\"tetuswap\",\"name\":\"Tetuswap\"},{\"id\":\"tfm\",\"name\":\"Terraformer\"},{\"id\":\"therocktrading\",\"name\":\"TheRockTrading\"},{\"id\":\"thorswap\",\"name\":\"THORSwap\"},{\"id\":\"thorus\",\"name\":\"Thorus\"},{\"id\":\"thorwallet\",\"name\":\"THORWallet DEX\"},{\"id\":\"tidex\",\"name\":\"Tidex\"},{\"id\":\"tinyman\",\"name\":\"Tinyman\"},{\"id\":\"tokenize\",\"name\":\"Tokenize\"},{\"id\":\"tokenlon\",\"name\":\"Tokenlon\"},{\"id\":\"tokenomy\",\"name\":\"Tokenomy\"},{\"id\":\"token_sets\",\"name\":\"TokenSets\"},{\"id\":\"toko_crypto\",\"name\":\"TokoCrypto\"},{\"id\":\"tokok\",\"name\":\"TOKOK\"},{\"id\":\"tokpie\",\"name\":\"Tokpie\"},{\"id\":\"tomb_swap_fantom\",\"name\":\"Tomb Swap (Fantom)\"},{\"id\":\"trade_ogre\",\"name\":\"TradeOgre\"},{\"id\":\"traderjoe\",\"name\":\"Trader Joe\"},{\"id\":\"traderjoe-v2-arbitrum\",\"name\":\"Trader Joe v2 (Arbitrum)\"},{\"id\":\"traderjoe-v2-avalanche\",\"name\":\"Trader Joe v2\"},{\"id\":\"tranquil_finance\",\"name\":\"Tranquil Finance\"},{\"id\":\"trisolaris\",\"name\":\"Trisolaris\"},{\"id\":\"tropical_finance\",\"name\":\"Tropical Finance\"},{\"id\":\"txbit\",\"name\":\"Txbit\"},{\"id\":\"ubeswap\",\"name\":\"Ubeswap\"},{\"id\":\"unicly\",\"name\":\"Unicly\"},{\"id\":\"uniswap_v2\",\"name\":\"Uniswap (v2)\"},{\"id\":\"uniswap_v3\",\"name\":\"Uniswap (v3)\"},{\"id\":\"uniswap_v3_arbitrum\",\"name\":\"Uniswap (Arbitrum One)\"},{\"id\":\"uniswap_v3_optimism\",\"name\":\"Uniswap (Optimism)\"},{\"id\":\"uniswap_v3_polygon_pos\",\"name\":\"Uniswap (Polygon)\"},{\"id\":\"uniwswap\",\"name\":\"UniWswap\"},{\"id\":\"unnamed\",\"name\":\"Unnamed\"},{\"id\":\"upbit\",\"name\":\"Upbit\"},{\"id\":\"upbit_indonesia\",\"name\":\"Upbit Indonesia \"},{\"id\":\"value_liquid_bsc\",\"name\":\"vSwap BSC\"},{\"id\":\"velic\",\"name\":\"Velic\"},{\"id\":\"velodrome\",\"name\":\"Velodrome Finance\"},{\"id\":\"verse\",\"name\":\"Verse\"},{\"id\":\"vindax\",\"name\":\"Vindax\"},{\"id\":\"viperswap\",\"name\":\"ViperSwap\"},{\"id\":\"vitex\",\"name\":\"ViteX\"},{\"id\":\"voltage_finance\",\"name\":\"Voltage Finance\"},{\"id\":\"voltswap_meter\",\"name\":\"Voltswap (Meter)\"},{\"id\":\"voltswap_theta\",\"name\":\"Voltswap (Theta)\"},{\"id\":\"vvs\",\"name\":\"VVS Finance\"},{\"id\":\"wagyuswap\",\"name\":\"WagyuSwap\"},{\"id\":\"wannaswap\",\"name\":\"Wannaswap\"},{\"id\":\"wanswap\",\"name\":\"WanSwap\"},{\"id\":\"waultswap_polygon\",\"name\":\"WaultSwap Polygon\"},{\"id\":\"wavelength\",\"name\":\"Wavelength\"},{\"id\":\"waves\",\"name\":\"Wx.network\"},{\"id\":\"wazirx\",\"name\":\"WazirX\"},{\"id\":\"wemix_fi\",\"name\":\"WEMIX.Fi\"},{\"id\":\"whitebit\",\"name\":\"WhiteBIT\"},{\"id\":\"wigoswap\",\"name\":\"Wigoswap\"},{\"id\":\"wombat\",\"name\":\"Wombat\"},{\"id\":\"woofi\",\"name\":\"WOOFi\"},{\"id\":\"woo_network_futures\",\"name\":\"WOO Network (Futures)\"},{\"id\":\"wootrade\",\"name\":\"WOO Network\"},{\"id\":\"xave\",\"name\":\"Xave Finance\"},{\"id\":\"xcad\",\"name\":\"XCAD DEX\"},{\"id\":\"xswap\",\"name\":\"XSwap\"},{\"id\":\"xt\",\"name\":\"XT.COM\"},{\"id\":\"xt_derivatives\",\"name\":\"XT.COM (Derivatives)\"},{\"id\":\"yobit\",\"name\":\"YoBit\"},{\"id\":\"yodeswap\",\"name\":\"Yodeswap\"},{\"id\":\"yokaiswap\",\"name\":\"Yokaiswap\"},{\"id\":\"yoshi_exchange_bsc\",\"name\":\"Yoshi.exchange (BSC)\"},{\"id\":\"yoshi_exchange_ftm\",\"name\":\"Yoshi.exchange (Fantom)\"},{\"id\":\"yunex\",\"name\":\"Yunex.io\"},{\"id\":\"zaif\",\"name\":\"Zaif\"},{\"id\":\"zappy\",\"name\":\"Zappy\"},{\"id\":\"zbg\",\"name\":\"ZBG\"},{\"id\":\"zbg_futures\",\"name\":\"ZBG Futures\"},{\"id\":\"zbx\",\"name\":\"ZBX\"},{\"id\":\"zebitex\",\"name\":\"Zebitex\"},{\"id\":\"zebpay\",\"name\":\"ZebPay\"},{\"id\":\"zenlink_astar\",\"name\":\"Zenlink (Astar)\"},{\"id\":\"zenlink_moonbeam\",\"name\":\"Zenlink (Moonbeam)\"},{\"id\":\"zenlink_moonriver\",\"name\":\"Zenlink (Moonriver)\"},{\"id\":\"zero_ex\",\"name\":\"0x Protocol\"},{\"id\":\"zigzag\",\"name\":\"ZigZag (zkSync v1)\"},{\"id\":\"zigzag_arbitrum\",\"name\":\"ZigZag (Arbitrum)\"},{\"id\":\"zilswap\",\"name\":\"ZilSwap\"},{\"id\":\"zipmex\",\"name\":\"Zipmex\"},{\"id\":\"zipswap\",\"name\":\"ZipSwap\"},{\"id\":\"zircon\",\"name\":\"Zircon\"}]")
            .Where(c => Regex.IsMatch(c["id"].Value<string>(),"^[a-zA-Z]"))
            .ToDictionary(c =>
                c["id"].Value<string>(),
                c => new RateSourceInfo(c["id"].ToString().ToLowerInvariant(), c["name"] + " (via CoinGecko)", $"https://api.coingecko.com/api/v3/exchanges/{c["id"]}/tickers", RateSource.Coingecko));
        }

        private readonly HttpClient Client;


        string _UnderlyingExchange;
        public string UnderlyingExchange
        {
            get
            {
                return _UnderlyingExchange;
            }
            set
            {
                _UnderlyingExchange = value;
                if (value is null)
                    RateSourceInfo = new("coingecko", "CoinGecko", "https://api.coingecko.com/api/v3/exchange_rates");
                else
                {
                    RateSourceInfo = SupportedExchanges[value];
                }
            }
        }

        public RateSourceInfo RateSourceInfo { get; private set; }

        public CoinGeckoRateProvider(IHttpClientFactory httpClientFactory)
        {
            UnderlyingExchange = null;
            Client = httpClientFactory.CreateClient();
            Client.BaseAddress = new Uri("https://api.coingecko.com/api/v3/");
            Client.DefaultRequestHeaders.Add("Accept", "application/json");
        }

        public virtual Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
        {
            return UnderlyingExchange is null ? GetCoinGeckoRates(cancellationToken) : GetCoinGeckoExchangeSpecificRates(1, cancellationToken);
        }

        private async Task<PairRate[]> GetCoinGeckoRates(CancellationToken cancellationToken)
        {
            using var resp = await GetWithBackoffAsync("exchange_rates", cancellationToken);
            resp.EnsureSuccessStatusCode();
            return JObject.Parse(await resp.Content.ReadAsStringAsync()).GetValue("rates").Children()
                .Where(token => ((JProperty)token).Name != "btc")
                .Select(token => new PairRate(new CurrencyPair("BTC", ((JProperty)token).Name.ToString()),
                    new BidAsk(((JProperty)token).Value["value"].Value<decimal>()))).ToArray();
        }

        private async Task<HttpResponseMessage> GetWithBackoffAsync(string request, CancellationToken cancellationToken)
        {
            TimeSpan retryWait = TimeSpan.FromSeconds(1);
retry:
            var resp = await Client.GetAsync(request, cancellationToken);
            if (resp.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
            {
                resp.Dispose();
                if (retryWait < TimeSpan.FromSeconds(60))
                {
                    await Task.Delay(retryWait, cancellationToken);
                    retryWait = TimeSpan.FromSeconds(retryWait.TotalSeconds * 2);
                    goto retry;
                }
                resp.EnsureSuccessStatusCode();
            }
            return resp;
        }

        private async Task<PairRate[]> GetCoinGeckoExchangeSpecificRates(int page, CancellationToken cancellationToken)
        {
            using var resp = await GetWithBackoffAsync($"exchanges/{UnderlyingExchange}/tickers?page={page}", cancellationToken);

            resp.EnsureSuccessStatusCode();
            List<PairRate> result = JObject.Parse(await resp.Content.ReadAsStringAsync()).GetValue("tickers")
                .Select(token => new PairRate(new CurrencyPair(token.Value<string>("base"), token.Value<string>("target")),
                    new BidAsk(token.Value<decimal>("last")))).ToList();
            if (page == 1 && resp.Headers.TryGetValues("total", out var total) &&
                resp.Headers.TryGetValues("per-page", out var perPage))
            {
                var totalItems = int.Parse(total.First());
                var perPageItems = int.Parse(perPage.First());

                var totalPages = totalItems / perPageItems;
                if (totalItems % perPageItems != 0)
                {
                    totalPages++;
                }

                var tasks = new List<Task<PairRate[]>>();
                for (int i = 2; i <= totalPages; i++)
                {
                    tasks.Add(GetCoinGeckoExchangeSpecificRates(i, cancellationToken));
                }

                foreach (var t in (await Task.WhenAll(tasks)))
                {
                    result.AddRange(t);
                }
            }

            return result.ToArray();
        }
    }
}
